/**
 * 
 */
package util;

import java.util.BitSet;

/**
 * @author nquangtrung
 *
 */
public class BitSequenceV2 {
	private int currentBitOffset;
	private BitSet bs;
	
	public static final int[] MASK = 
		{ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,		// 1st byte's masks
		  0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000,		// 2nd byte's masks
		  0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000,		// 3rd byte's masks
		  0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000 }; 	// 4th byte's masks
	
	/**
	 * Define some constant for stuffing size
	 */
	public static final int MAX_BYTE_STUFFING_SIZE = 8;
	public static final int MAX_INT_STUFFING_SIZE = 32;
	
	public BitSequenceV2(int capacity) {
		this.setBitSet(new BitSet(capacity));
		this.setCurrentBitOffset(0);
	}
	
	public int stuff(byte b, int size) {
		if (size <= MAX_BYTE_STUFFING_SIZE) {
			boolean[] bitAr = byteToBool(b);
			BitSet bs = this.getBitSet();
			/**
			 * Start stuffing from msd -> lsd
			 */
			for (int i = size-1; i >= 0; i--) {
				bs.set(getCurrentBitOffset(), bitAr[i]);
				moveCurrentBitOffset();
			}
			return size;
		} else {
			return -1;
		}
	}
	public int stuff(int x, int size) {
		if (size <= MAX_INT_STUFFING_SIZE) {
			boolean[] bitAr = intToBool(x);
			BitSet bs = this.getBitSet();
			/**
			 * Start stuffing from msd -> lsd
			 */
			for (int i = size-1; i >= 0; i--) {
				bs.set(getCurrentBitOffset(), bitAr[i]);
				moveCurrentBitOffset();
			}
			return size;
		} else {
			return -1;
		}
	}
	public int getSize() {
		return this.getBitSet().size();
	}
	public byte[] makeByte() {
		int size = this.getSize();
		BitSet bs = this.getBitSet();
		byte[] b = new byte[size];
		int b_index = -1;
		for (int i = 0; i < size; i++) {
			if (i % 8 == 0) b_index += 1;  
			b[b_index] = (byte) ((b[b_index] * 2) + (bs.get(i)?1:0));
			
		}
		return b;
	}
	public boolean[] intToBool(int x) {
		boolean[] ar = new boolean[MAX_INT_STUFFING_SIZE];
		/**
		 * ar[0] -> least significant digit
		 * ar[15] -> most significant digit
		 */
		for (int i = 0; i < MAX_INT_STUFFING_SIZE; i++) {
			boolean cb = ((x & MASK[i]) != 0);
			ar[i] = cb;
		}
		
		return ar;
	}
	public boolean[] byteToBool(byte b) {
		boolean[] ar = new boolean[MAX_BYTE_STUFFING_SIZE];
		/**
		 * ar[0] -> least significant digit
		 * ar[7] -> most significant digit
		 */
		for (int i = 0; i < MAX_BYTE_STUFFING_SIZE; i++) {
			boolean cb = ((b & MASK[i]) != 0);
			ar[i] = cb;
		}
		
		return ar;
	}
	/**
	 * @return the bs
	 */
	public BitSet getBitSet() {
		return bs;
	}
	/**
	 * @param bs the bs to set
	 */
	public void setBitSet(BitSet bs) {
		this.bs = bs;
	}
	/**
	 * @return the currentBitOffset
	 */
	public int getCurrentBitOffset() {
		return currentBitOffset;
	}
	/**
	 * @param currentBitOffset the currentBitOffset to set
	 */
	public void setCurrentBitOffset(int currentBitOffset) {
		this.currentBitOffset = currentBitOffset;
	}
	public void moveCurrentBitOffset() {
		this.moveCurrentBitOffset(1);
	}
	public void moveCurrentBitOffset(int step) {
		this.setCurrentBitOffset(this.getCurrentBitOffset() + step);
	}
}
